package com.disapand.dingdang.util

import com.disapand.dingdang.extension.generateURI
import com.disapand.dingdang.model.Resource
import com.disapand.dingdang.service.ResourceService
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import org.springframework.util.ClassUtils
import org.springframework.web.bind.annotation.*

/**
 * @author disapand
 * @date 2022/2/27 - 10:09 星期日
 */

/**
 * 系统启动时进行的操作
 *
 * @property applicationContext
 * @property resourceService
 */
@Component
class ResourcesLoader(
    private val applicationContext: ApplicationContext,
    private val resourceService: ResourceService
) : ApplicationRunner {

    override fun run(args: ApplicationArguments?) {
        /**
         * 获取所有以 RestController 注解的控制器类
         */
        val beanMap = applicationContext.getBeansWithAnnotation(RestController::class.java)
        val resourceList = mutableListOf<Resource>()
        /**
         * 清空已经保存的 resources 信息
         */
        resourceService.truncate()
        beanMap.forEach {
            /**
             * 获取控制器类的原始类
             * it.value.javaClass 获取到的有可能是 CGLIB 代理的类
             * 所以采用 hutools 提供的 ClassUtils.getUserClass() 方法
             */
            val bean = ClassUtils.getUserClass(it.value.javaClass)

            /**
             * 获取控制器中有 RequestMapping 注解的类
             */
            val resourcesGroup = bean.getAnnotation(RequestMapping::class.java)

            /**
             * 获取类中的方法
             */
            val resources = bean.methods
            resources.forEach resourceHandle@{ resource ->
                /**
                 * 获取类中以 RequestMapping, GetMapping, PostMapping, PutMapping, DeleteMapping 注解的类
                 */
                val requestResource = resource.getAnnotation(RequestMapping::class.java)
                val getResource = resource.getAnnotation(GetMapping::class.java)
                val postResource = resource.getAnnotation(PostMapping::class.java)
                val putResource = resource.getAnnotation(PutMapping::class.java)
                val deleteResource = resource.getAnnotation(DeleteMapping::class.java)

                /**
                 * 分别从 *Mapping 注解的类中获取 resources 的请求方法、请求路径和请求名称
                 */
                when {
                    requestResource != null -> {
                        val methodList = mutableListOf<String>()
                        requestResource.method.forEach { method ->
                            methodList.add(method.name)
                        }
                        val generateResources = generateResources(
                            resourcesGroup.name, resourcesGroup.value,
                            methodList.toTypedArray(), requestResource.name, requestResource.value
                        )
                        resourceList.addAll(generateResources)
                    }
                    getResource != null -> {
                        val generateResources = generateResources(
                            resourcesGroup.name, resourcesGroup.value,
                            arrayOf("GET"), getResource.name, getResource.value
                        )
                        resourceList.addAll(generateResources)
                    }
                    postResource != null -> {
                        val generateResources = generateResources(
                            resourcesGroup.name, resourcesGroup.value,
                            arrayOf("POST"), postResource.name, postResource.value
                        )
                        resourceList.addAll(generateResources)
                    }
                    putResource != null -> {
                        val generateResources = generateResources(
                            resourcesGroup.name, resourcesGroup.value,
                            arrayOf("PUT"), putResource.name, putResource.value
                        )
                        resourceList.addAll(generateResources)
                    }
                    deleteResource != null -> {
                        val generateResources = generateResources(
                            resourcesGroup.name, resourcesGroup.value,
                            arrayOf("DELETE"), deleteResource.name, deleteResource.value
                        )
                        resourceList.addAll(generateResources)
                    }
                    else -> {
                        return@resourceHandle
                    }
                }
            }
        }

        /**
         * 将 resources 批量保存至数据库
         */
        resourceService.batchSave(resourceList)
    }

    fun generateResources(
        groupName: String, group: Array<String>,
        method: Array<String>, resourceName: String, resources: Array<String>
    ): MutableList<Resource> {
        val environment = applicationContext.environment
        val prefix = environment.getProperty("server.servlet.context-path") ?: ""
        val results = mutableListOf<Resource>()

        group.forEach { g ->
            method.forEach { m ->
                resources.forEach { r ->
                    results.add( Resource {
                        this.group = g.generateURI()
                        this.groupName = groupName
                        this.method = m
                        this.resource = "${prefix.generateURI()}${g.generateURI()}${r.generateURI()}"
                        this.resourceName = resourceName
                    })
                }
            }
        }

        return results
    }
}